#include "client.h"
#include <stdio.h>
#include <string.h>

extern char jwt[JWT_LEN];
int longfd = -1;//长命令sockfd，-1表示还没有连接
void epoll_add(int epfd, int fd) {
  struct epoll_event event;
  event.events = EPOLLIN;
  event.data.fd = fd;
  epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event);
}

int tcp_init(char *ip, char *port) {

  int sockfd = socket(AF_INET, SOCK_STREAM, 0);

  struct sockaddr_in addr;
  addr.sin_family = AF_INET;
  addr.sin_port = htons(atoi(port));
  addr.sin_addr.s_addr = inet_addr(ip);

  connect(sockfd, (struct sockaddr *)&addr, sizeof(addr));

  return sockfd;
}

ssize_t sendn(int fd, const void *buf, ssize_t length) {
  char *p = (char *)buf;
  ssize_t total = 0;
  while (total < length) {
    ssize_t send_ret = send(fd, p + total, length - total, MSG_NOSIGNAL);
    if (send_ret <= 0) {
      fprintf(stderr, "client disconnect. send abort.\n");
      return send_ret;
    }
    total += send_ret;
  }
  return total;
}

ssize_t recvn(int fd, void *buf, ssize_t len) {
  char *p = (char *)buf;
  ssize_t total = 0;
  while (total < len) {
    int recv_ret = recv(fd, p + total, len - total, 0);
    if (recv_ret <= 0) {
      fprintf(stderr, "server disconnect. recv abort.\n");
      return recv_ret;
    }
    total += recv_ret;
  }
  return total;
}

char *command[] = {"cd",    "ls",   "pwd", "mkdir", 
		   "rmdir", "login", "signup", "tree"};
void *(*f[])(void *) = {cd_c,    ls_c,   pwd_c,mkdir_c, 
		        rmdir_c, login_c, sign_up_c, tree_c};

char *cmd_long[] = {"puts", "gets"};
void* (*l[])(void*) = {puts_thread, gets_thread };

int deal_command(int netfd, char *ip, char *longport, char *cmd) {
//int deal_command(int netfd, char *cmd) {
  char cmd_copy[4096] = {0};
  char *div_cmd[1] = {0};
  char *div_para[1024] = {0};
  strcpy(cmd_copy, cmd);

  div_cmd[0] = strtok(cmd_copy, " ");
  if (!div_cmd[0]) {
    return -1;
  }

  char *token;
  int idx = 0;
  while ((token = strtok(NULL, " \n")) != NULL) {
    div_para[idx++] = token;
  }
  if (!idx) {
    return -1;
  }
  div_para[idx] = NULL;
 //匹配长命令
    int nLong = sizeof(cmd_long) / sizeof(cmd_long[0]);

    for (int i = 0; i < nLong; i++) {
        if (strcmp(cmd_long[i], div_cmd[0]) == 0) {
            //如果尚未连接长命令端口，则现在连接
            if (longfd == -1) {
                longfd = tcp_init(ip, longport);
            }
            
            Para *para = (Para *) calloc(1, sizeof(Para));
            para->netfd = longfd;
            para->cmd = strdup(cmd);
            para->path = (char **) calloc(idx + 1, sizeof(char *));
            for (int j = 0; j < idx; j++) {
                para->path[j] = strdup(div_para[j]);
            }
     
            pthread_t tid;
            pthread_create(&tid, NULL, l[i], para);
            pthread_detach(tid);
            return 0;
        }
    }
    
  int n = sizeof(command) / sizeof(command[0]);

  for (int i = 0; i < n; i++) {
    if (strcmp(command[i], div_cmd[0]) == 0) {
      Para para;
      para.netfd = netfd;
      para.cmd = cmd;
      para.path = div_para;
      f[i](&para);
      return 0;
    }
  }
  return -1;
}

// void send_tlv(int sockfd, void *buf, ssize_t len) {
//   TLV tlv;
//   memset(&tlv, 0, sizeof(TLV));
//   strcpy(tlv.jwt, jwt);
//   printf("%s\n",tlv.jwt);
//   tlv.len = len;
//   memcpy(tlv.data, buf, len);
//   send(sockfd, &tlv, tlv.len + JWT_LEN + sizeof(tlv.len), MSG_NOSIGNAL);
//   // printf("len: %d data: %s\n",tlv.len,tlv.data);
// }

ssize_t send_tlv(int netfd, const void *buf, ssize_t len) {
  // 1. 发送长度字段
  ssize_t net_len = len;
  if (sendn(netfd, &net_len, sizeof(net_len)) != sizeof(net_len)) {
    return -1;
  }
  // 2. 发送数据部分
  if (sendn(netfd, buf, len) != len) {
    return -1;
  }
  // 3. 发送JWT
  if (sendn(netfd, jwt, JWT_LEN) != JWT_LEN) {
    return -1;
  }

  return len + JWT_LEN; // 返回总发送字节数
}

ssize_t recv_tlv(int netfd, TLV *tlv) {
  ssize_t len;
  ssize_t ret;

  // 接收长度字段
  ret = recvn(netfd, &len, sizeof(ssize_t));
  if (ret != sizeof(ssize_t)) {
    // 连接断开或错误（ret <= 0）
    return ret;
  }
  tlv->len = len;
  // 接收数据部分
  ret = recvn(netfd, tlv->data, tlv->len);
  if (ret != tlv->len) {
    return -1; // 数据接收不完整或断开
  }
  // 接收JWT
  ret = recvn(netfd, tlv->jwt, JWT_LEN);
  if (ret != JWT_LEN) {
    return -1; // JWT接收不完整或断开
  }
  tlv->jwt[JWT_LEN] = '\0'; // 确保字符串终止

  return tlv->len + JWT_LEN; // 返回总接收字节数
}

// ssize_t recv_tlv(int netfd, TLV *tlv) {
//   ssize_t len;
//   char buf[4096] = {0};
//   ssize_t ret_recv = recvn(netfd, &len, sizeof(ssize_t));
//   if (ret_recv <= 0) {
//     return 0;
//   }
//   memcpy(&tlv->len, &len, sizeof(ssize_t));
//   ssize_t ret_recv2 = recvn(netfd, buf, tlv->len);
//   memcpy(tlv->data, buf, tlv->len);
//   memset(buf, 0, sizeof(buf));
//   recvn(netfd, buf, JWT_LEN);
//   strcpy(tlv->jwt, buf);
//   return ret_recv2;
// }

void *cd_c(void *arg) {
  Para *para = (Para *)arg;
  send_tlv(para->netfd, para->cmd, strlen(para->cmd));
  // 打日志
  TLV tlv;
  memset(&tlv, 0, sizeof(TLV));
  recv_tlv(para->netfd, &tlv);
  printf("%s\n", tlv.data);
  return NULL;
}

void *ls_c(void *arg) {
  Para *para = (Para *)arg;
  send_tlv(para->netfd, para->cmd, strlen(para->cmd));
  // 打日志
  TLV tlv;
  memset(&tlv, 0, sizeof(TLV));
  recv_tlv(para->netfd, &tlv);
  printf("%s\n", tlv.data);
  return NULL;
}

void *puts_c(void *arg) {
  Para *para = (Para *)arg;
    TLV tlv;
    off_t offset = 0;
    send_tlv(para->netfd, para->cmd, strlen(para->cmd));

    char file_name[1024] = {0};
    char *p = strtok(para->path[0], "/");
    while(1){
        bzero(file_name, sizeof(file_name));
        memcpy(file_name, p, strlen(p));
        p = strtok(NULL, "/");
        if(p == NULL){
            break;
        }

    }

    char *hash_val = get_hash(file_name, "md5");
    send_tlv(para->netfd, hash_val, strlen(hash_val));
    printf("%s\n", hash_val);

    recv_tlv(para->netfd, &tlv);
    char flag;
    memcpy(&flag, tlv.data, 1);
    if(flag == 'F'){
        printf("秒传成功.\n");
        return NULL;
    }
    if(flag == 'R'){
        printf("开启断点续传.\n");

        // 接收已存在大小
        off_t existing_size = 0;
        recv_tlv(para->netfd, &tlv);
        memcpy(&existing_size, tlv.data, tlv.len);

        // 计算剩余大小

        int fd = open(file_name, O_RDWR);
        struct stat file_stat;
        fstat(fd, &file_stat);
        off_t file_size = file_stat.st_size;

        off_t remaing_size = file_size - existing_size;
        send_tlv(para->netfd, &remaing_size, sizeof(remaing_size));

        lseek(fd, existing_size, SEEK_SET);

        if(remaing_size < MAX_SIZE){
            while(1){
                char buffer[4096] = {0};
                ssize_t read_num = read(fd, buffer, sizeof(buffer));
                send_tlv(para->netfd, buffer, read_num);
                if(read_num == 0){
                    break;
                }
            }

        }else{
            char *mp = (char *)mmap(NULL, remaing_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, existing_size);
            send(para->netfd, mp, remaing_size, MSG_NOSIGNAL);
            munmap(mp, remaing_size);
        }


        return NULL;
    }

    //新文件上传
    printf("新文件上传.\n");
    int fd = open(file_name, O_RDONLY);
    off_t file_size = 0;
    struct stat file_stat;
    fstat(fd, &file_stat);
    file_size = file_stat.st_size;

    // 传文件大小
    send_tlv(para->netfd, &file_size, sizeof(file_size));

    if(file_size < MAX_SIZE){
        while(1){
            char buffer[4096] = {0};
            ssize_t read_num = read(fd, buffer, sizeof(buffer));
            send_tlv(para->netfd, buffer, read_num);
            if(read_num == 0){
                printf("上传成功.\n");
                break;
            }
        }
    }else{
        char *mp = (char *)mmap(NULL, file_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
        send(para->netfd, mp, file_size, MSG_NOSIGNAL);
        munmap(mp, file_size);
    }


    return NULL;
}

void *gets_c(void *arg) {
  Para *para = (Para *)arg;
    TLV tlv;
    send_tlv(para->netfd, para->cmd, strlen(para->cmd));

    char download_path[1024] = {0};
    char *p = strtok(para->path[0], "/");
    while(1){
        bzero(download_path, sizeof(download_path));
        strcpy(download_path, p);
        p = strtok(NULL, "/");
        if(p == NULL){
            break;
        }
    }
    printf("%s\n", download_path);

    off_t existing_size = 0;
    int fd;

    if(access(download_path, F_OK) == 0){
        struct stat file_stat;
        stat(download_path, &file_stat);
        existing_size = file_stat.st_size;

        // 发送文件大小给服务端
        send_tlv(para->netfd, &existing_size, sizeof(existing_size));

        fd = open(download_path, O_RDWR | O_APPEND);
    }else{// 发新的
        existing_size = 0;
        send_tlv(para->netfd, &existing_size, sizeof(existing_size));

        fd = open(download_path, O_RDWR | O_CREAT | O_TRUNC, 0777);
    }

    off_t remainging_size;
    recv_tlv(para->netfd, &tlv);
    memcpy(&remainging_size, tlv.data, tlv.len);

    if(remainging_size == 0){
        close(fd);
        printf("文件已经存在.\n");
        return NULL;
    }

    if(remainging_size < MAX_SIZE){
        while(remainging_size > 0){
            recv_tlv(para->netfd, &tlv);
            write(fd, tlv.data, tlv.len);
            remainging_size -= tlv.len;
        }
    }
    else{
        ftruncate(fd, existing_size + remainging_size);
        char *mp = (char *)mmap(NULL, remainging_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, existing_size);
        recv(para->netfd, mp, remainging_size, MSG_WAITALL);
        munmap(mp, remainging_size);
    }

    return NULL;
}

void *pwd_c(void *arg) {
  Para *para = (Para *)arg;
  send_tlv(para->netfd, para->cmd, strlen(para->cmd));
  // 打日志
  TLV tlv;
  memset(&tlv, 0, sizeof(TLV));
  recv_tlv(para->netfd, &tlv);
  printf("%s\n", tlv.data);
  return NULL;
}

void *mkdir_c(void *arg) {
  Para *para = (Para *)arg;
  send_tlv(para->netfd, para->cmd, strlen(para->cmd));
  // 打日志
  TLV tlv;
  memset(&tlv, 0, sizeof(TLV));
  recv_tlv(para->netfd, &tlv);
  printf("%s\n", tlv.data);
  return NULL;
}

void *rmdir_c(void *arg) {
  Para *para = (Para *)arg;
  send_tlv(para->netfd, para->cmd, strlen(para->cmd));

  // 打日志
  printf("%d %s\n", para->netfd, para->cmd);
  return NULL;
}

void *login_c(void *arg) {
  Para *para = (Para *)arg;
  send_tlv(para->netfd, para->cmd, strlen(para->cmd));

  TLV tlv;
  recv_tlv(para->netfd, &tlv);
  strcpy(jwt, tlv.data);

  printf("server: %s\n", jwt);

  // 打日志
  // printf("%d %s %s\n", para->netfd, para->cmd[0], para->cmd[1]);

  return NULL;
}

void *sign_up_c(void *arg) {
  Para *para = (Para *)arg;
  send_tlv(para->netfd, para->cmd, strlen(para->cmd));

  //   TLV tlv;
  //   recv_tlv(para->netfd, &tlv);
  //   strcpy(jwt, tlv.jwt);
  //   printf("server: %s\n", tlv.data);
  return NULL;
}

void *tree_c(void *arg) {
  Para *para = (Para *)arg;
  send_tlv(para->netfd, para->cmd, strlen(para->cmd));

  TLV tlv;
  recv_tlv(para->netfd, &tlv);
  printf("server: %s\n", tlv.data);
  return NULL;
}
void* puts_thread(void* arg) {
    Para* para = (Para*)arg;
    puts_c(arg);
    if (para->cmd) {
        free(para->cmd);
    }
    if (para->path) {
        for (int i = 0; para->path[i]; i++) {
            free(para->path[i]);
        }
        free(para->path);
    }
	free(para);
    pthread_exit(NULL);
}

void* gets_thread(void* arg) {
    Para* para = (Para*)arg;
    gets_c(arg);
    if (para->cmd) {
        free(para->cmd);
    }
    if (para->path) {
        for (int i = 0; para->path[i]; i++) {
            free(para->path[i]);
        }
        free(para->path);
    }
	free(para);
    pthread_exit(NULL);
}
